
// Compute 3 cross sectional representations of the table
// The only parameter is boolean, it determines if we should skip filled cells.
SudokuGrid.prototype.computeCrosssection = function (skipFilled)
{
	var octopus = { row : [], col: [], box: [] };
	for (var i = 0; i < 9; i++)
	{
		octopus.row[i] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // row
		octopus.col[i] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // col
		octopus.box[i] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // box
	}
	
	for (var r = 0; r < 9; r++)
	{
		for (var c = 0; c < 9; c++)
		{
			if (skipFilled && !this.isEmpty(r, c)) continue;

			var b = CalcBoxNum(r, c);
			var p = (r % 3 * 3) + (c % 3);
			var candidates = this.notations[r][c];
			
			// Loop through all the candidates of a cell
			while (candidates)
			{
				var digit = (candidates & ~(candidates - 1)).bitmaskToDigit() - 1;
				octopus.row[r][digit] |= (1 << c);
				octopus.col[c][digit] |= (1 << r);
				octopus.box[b][digit] |= (1 << p);

				candidates &= candidates - 1;
			}
		}
	}
	return octopus;
}

// Fill in Singles
// Singles are those cells with only one candidate

// Error checking perfomed: Make sure candidate count is not zero. 
SudokuGrid.prototype.strategySingles = function ()
{
	var notation, changed = false;
	for (var r = 0; r < 9; r++)
		for (var c = 0; c < 9; c++)
		{
			if(this.isEmpty(r, c))
				changed |= this.strategySingles_SingleCell(r, c);
		}

		return changed;
}

SudokuGrid.prototype.strategySingles_SingleCell = function (r, c)
{
	var notation = this.notations[r][c];
	if (notation === 0) throw new Error('Cell (' + r + ', ' + c + ') has no valid candidates.');

	if (notation.isPowerOfTwo())
	{
		this.enforceMask(r, c, ~notation);
		this.setDigitByBitvalue(r, c, notation)
		return true;
	}
	return false;
}

// Find and fill in Hidden Singles
// A Hidden Single is a candidate that only appear once in a scope.

// Error checking perfomed: 1) Make sure candidate count is not zero. 2) Make sure the scope is not missing multiple candidates.
SudokuGrid.prototype.strategyHiddenSingles = function ()
{
	var changed = false;
	var crosssection = this.computeCrosssection(true);

	for (var f = 0; f < 9; f++)
		for (var bitpos = 0; bitpos < 9; bitpos++)
		{
			if (crosssection.row[f][bitpos] !== 0 && crosssection.row[f][bitpos].isPowerOfTwo())
			{
				var r = f;
				var c = crosssection.row[f][bitpos].bitmaskToDigit() - 1; 
				var CellValue = (1 << bitpos);

				// console.log('Hidden single: Row (' + r + ', ' + c +')' + this.notations[r][c].bitmaskToString() + ' ' + CellValue.bitmaskToDigit());
				this.setNotation(r, c, CellValue);
				this.enforceMask(r, c, ~CellValue);
				this.setDigitByBitvalue(r, c, this.notations[r][c])
				changed =  true;
			}

			if (crosssection.col[f][bitpos] !== 0 && crosssection.col[f][bitpos].isPowerOfTwo())
			{
				var r = crosssection.col[f][bitpos].bitmaskToDigit() - 1; 
				var c = f;
				var CellValue = (1 << bitpos);

				// console.log('Hidden single: Col (' + r + ', ' + c +')' + this.notations[r][c].bitmaskToString() + ' ' + CellValue.bitmaskToDigit());
				this.setNotation(r, c, CellValue);
				this.enforceMask(r, c, ~CellValue);
				this.setDigitByBitvalue(r, c, this.notations[r][c])
				changed =  true;
			}

			if (crosssection.box[f][bitpos] !== 0 && crosssection.box[f][bitpos].isPowerOfTwo())
			{
				var pos = crosssection.box[f][bitpos].bitmaskToDigit() - 1;
				var r = CalcBoxNum(f, pos);
				var c = (f % 3) * 3 + (pos % 3);
				var CellValue = (1 << bitpos);

				// console.log('Hidden single: Box (' + r + ', ' + c +')' + this.notations[r][c].bitmaskToString() + ' ' + CellValue.bitmaskToDigit());
				this.setNotation(r, c, CellValue);
				this.enforceMask(r, c, ~CellValue);
				this.setDigitByBitvalue(r, c, this.notations[r][c])
				changed =  true;
			}
		}
	return changed;
}

// Premise: For a given cell, the combined candidates set in each of its 3 scopes must contain 8 or 9 numbers. 
//          If it is 8, then the current cell is a hidden single.
SudokuGrid.prototype.strategyHiddenSingles_SingleCell = function (r, c)
{
	// hidden single test
	var IntersectionMask = this.strategyHiddenSingles_CalcMask(r, c);

	if (IntersectionMask === 511) return false;

	// get hidden single value
	var CellValue = 511 & ~IntersectionMask;

	if (!CellValue.isPowerOfTwo()) 
	{
		throw new Error('Error: Hidden Single calculation yielded multiple hidden singles for single cell (' + r + ', ' + c + '):' + CellValue.toString(2));
	}
	if ((this.notations[r][c] & CellValue) === 0)
	{
		throw new Error('Error: Calculated Hidden Single is invalid (' + r + ',' + c + '):' + CellValue.bitmaskToDigit(2) + 'vs' + this.notations[r][c].bitmaskToString());
	}

	console.log('Hidden single (' + r + ', ' + c +')');
	this.setNotation(r, c, CellValue);
	this.enforceMask(r, c, IntersectionMask);
	this.setDigitByBitvalue(r, c, this.notations[r][c])
	return true;
}

// Intersection mask
SudokuGrid.prototype.strategyHiddenSingles_CalcMask = function (row, col)
{
	var IntersectionMaskRow = 0, IntersectionMaskCol = 0, IntersectionMaskBox = 0;

	for (var i = 0; i < 9; i++)
	{
		if (i !== col) IntersectionMaskRow |= this.notations[row][i];
		if (i !== row) IntersectionMaskCol |= this.notations[i][col];
	}

	if ((IntersectionMaskRow & IntersectionMaskCol) !== 511) return IntersectionMaskRow & IntersectionMaskCol;

	var HomeRow = CalcBoxHomeCell(row);
	var HomeCol = CalcBoxHomeCell(col);

	for (var r = HomeRow; r < HomeRow + 3; r++)
		for (var c = HomeCol; c < HomeCol + 3; c++)
			if ((r !== row) || (c !== col)) // NOTE: this should be OR, because we are counting every cell in the box except current cell.
				IntersectionMaskBox |= this.notations[r][c];

	// Combined mask: if a candidate does not appear in one, then it should not appear in the others, so we AND.
	return IntersectionMaskRow & IntersectionMaskCol & IntersectionMaskBox; 
}
